package com.xyzwps.example.chain;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

public record CollectionChain<T>(Collection<T> value) {

    public CollectionChain<T> filter(Predicate<T> predicate) {
        var fc = new LinkedList<T>();
        for (var it : this.value) {
            if (predicate.test(it)) {
                fc.add(it);
            }
        }
        return new CollectionChain<>(fc);
    }

    public <R> CollectionChain<R> map(Function<T, R> fn) {
        var fc = new LinkedList<R>();
        for (var it : this.value) {
            fc.add(fn.apply(it));
        }
        return new CollectionChain<>(fc);
    }

    public <R> CollectionChain<R> flatMap(Function<T, CollectionChain<R>> fn) {
        var fc = new LinkedList<R>();
        for (var it : this.value) {
            fn.apply(it).reduce(fc, (r, rc) -> {
                rc.add(r);
                return rc;
            });
        }
        return new CollectionChain<>(fc);
    }

    public <K> MapChain<K, T> keyBy(Function<T, K> toKey) {
        var map = new HashMap<K, T>();
        for (var it : this.value) {
            map.put(toKey.apply(it), it);
        }
        return new MapChain<>(map);
    }


    public <R> SingleChain<R> reduce(R init, BiFunction<T, R, R> acc) {
        var r = init;
        for (var it : this.value) {
            r = acc.apply(it, r);
        }
        return new SingleChain<>(r);
    }
}
